/*
 * BlkIndex2NumTranslator.cpp
 *
 *  Created on: May 12, 2010
 *      Author: lukasz
 */

#include "BlkIndex2NumTranslator.h"
#include "FSException.h"
#include "SuperBlock.h"
#include "BlockReader.h"

BlkIndex2NumTranslator::BlkIndex2NumTranslator(BlockManager& reader, SuperBlock& sb)
	:d_blocks(new uint32_t[8*1024])// TODO parametrize the buffer length
	 ,d_reader(reader)
	 ,d_sb(sb)
{
}

/*****************************************************************************/
/*****************************************************************************/

BlkIndex2NumTranslator::~BlkIndex2NumTranslator() {
	delete[] d_blocks;
}

/*****************************************************************************/
/*****************************************************************************/

uint32_t BlkIndex2NumTranslator::index2Num(InodePtr inode, uint32_t block_index)
{
	uint32_t direct_blocks = directBlkNum();
	const uint32_t indirect_blocks = indirectBlkNum();
	uint32_t double_indirect = doubleIndirectBlkNum();
	uint32_t triple_indirect = tripleIndirectBlkNum();

	if (block_index < direct_blocks)
	{
		return inode->d_block[block_index];
	}

	if ((block_index-= direct_blocks) < indirect_blocks)
	{
		uint32_t entry = loadBlocsFromIndirectSpace(inode, block_index);
		return d_blocks[entry];
	}

	if ( (block_index -= indirect_blocks) < double_indirect )
	{
		uint32_t entry = loadBlocksFromDoubleIndirectSpace(inode, block_index);
		return d_blocks[entry];
	}

	if ( (block_index -= double_indirect) < triple_indirect )
	{
		uint32_t entry = loadBlocksFromTripleIndirectSpace(inode, block_index);
		return d_blocks[entry];
	}

	throw FSException(EFAULT, "Bad block index");
}

/*****************************************************************************/
/*****************************************************************************/

uint32_t BlkIndex2NumTranslator::getBlkNumRange(
									InodePtr inode,
									uint32_t start_index,
									uint32_t* buffer_block)
{
	uint32_t direct_blocks = directBlkNum();
	const uint32_t indirect_blocks = indirectBlkNum();
	uint32_t double_indirect = doubleIndirectBlkNum();
	uint32_t triple_indirect = tripleIndirectBlkNum();
	uint32_t block_size = d_sb.d_blockSize;

	if (start_index < direct_blocks)
	{
		memcpy(buffer_block, inode->d_block, direct_blocks*sizeof(uint32_t));
		return start_index;
	}

	if ((start_index-= direct_blocks) < indirect_blocks)
	{
		uint32_t entry = loadBlocsFromIndirectSpace(inode, start_index);
		memcpy(buffer_block, d_blocks, block_size);
		return entry;
	}

	if ( (start_index -= indirect_blocks) < double_indirect )
	{
		uint32_t entry = loadBlocksFromDoubleIndirectSpace(inode, start_index);
		memcpy(buffer_block, d_blocks, block_size);
		return entry;
	}

	if ( (start_index -= double_indirect) < triple_indirect )
	{
		uint32_t entry = loadBlocksFromTripleIndirectSpace(inode, start_index);
		memcpy(buffer_block, d_blocks, block_size);
		return entry;
	}

	throw FSException(ENOSPC, "No free blocks.");
}

/*****************************************************************************/
/*****************************************************************************/

uint32_t BlkIndex2NumTranslator::loadBlocsFromIndirectSpace(
								InodePtr inode, uint32_t block_index)
{
	assert(inode->d_block[INDIRECT_1_INDEX]);
	d_reader.getBlock(inode->d_block[INDIRECT_1_INDEX], d_blocks);
	return block_index;
}

/*****************************************************************************/
/*****************************************************************************/

uint32_t  BlkIndex2NumTranslator::loadBlocksFromDoubleIndirectSpace(
									InodePtr inode, uint32_t block_index)
{
	uint32_t* inodes = (uint32_t*) d_blocks;
	uint32_t addr_per_block = indirectBlkNum();
	assert((d_sb.d_blockSize>1024) || ((d_sb.d_blockSize==1024) && (block_index < addr_per_block*addr_per_block) ));


	uint32_t block1 = block_index / addr_per_block;
	uint32_t entry = block_index % addr_per_block;

	d_reader.getBlock(inode->d_block[INDIRECT_2_INDEX], d_blocks);
	assert(inodes[block1] > 0);
	d_reader.getBlock(inodes[block1], d_blocks);
	assert(inodes[entry] > 0);
	return entry;
}

/*****************************************************************************/
/*****************************************************************************/

uint32_t BlkIndex2NumTranslator::loadBlocksFromTripleIndirectSpace(
									InodePtr inode, uint32_t p_block_index)
{
	uint32_t block_index = p_block_index;
	uint32_t* inodes = (uint32_t*) d_blocks;
	uint32_t addr_per_block = indirectBlkNum();
	uint32_t block1 = block_index / (addr_per_block * addr_per_block);
	block_index = (block_index - block1 * addr_per_block * addr_per_block) ;
	uint32_t block2 = block_index / addr_per_block;
	uint32_t entry = block_index % addr_per_block;

	d_reader.getBlock(inode->d_block[INDIRECT_3_INDEX], d_blocks);
	assert(inodes[block1] > 0);
	d_reader.getBlock(inodes[block1], d_blocks);
	assert(inodes[block2] > 0);
	d_reader.getBlock(inodes[block2], d_blocks);
	assert(inodes[entry] > 0);
	return entry;
}

/*****************************************************************************/
/*****************************************************************************/

uint32_t  BlkIndex2NumTranslator::directBlkNum() const
{
	return DIRECT_LAST_INDEX +1;
}

/*****************************************************************************/
/*****************************************************************************/

uint32_t  BlkIndex2NumTranslator::indirectBlkNum() const
{
	return d_sb.d_blockSize/sizeof(uint32_t);
}

/*****************************************************************************/
/*****************************************************************************/

uint32_t  BlkIndex2NumTranslator::doubleIndirectBlkNum() const
{
	const uint32_t indirect_blocks = d_sb.d_blockSize/sizeof(uint32_t);
	return indirect_blocks*indirect_blocks;
}

/*****************************************************************************/
/*****************************************************************************/

uint32_t  BlkIndex2NumTranslator::tripleIndirectBlkNum() const
{
	const uint32_t indirect_blocks = d_sb.d_blockSize/sizeof(uint32_t);
	uint32_t double_indirect = indirect_blocks*indirect_blocks;
	return double_indirect*indirect_blocks;
}
